/* Arduino touch library v0.2
 * Copyright (C) 2011 Manfred Fuchs
 * www.mafu-foto.de
 * manfred@fuchsrudel.de
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#undef abs
#include <MI0283QT2/MI0283QT2.h>
#include <ADS7846/ADS7846.h>
#include <MI0283QT2/fonts.h>
// include this library's description file
#include "TouchLib.h"
#include "Arduino.h"


// *** private functions *****************************************************

void Touch::buttonDraw(TouchButtonPtr _button, bool _down)
{
  uint16_t col;

  if (_button != NULL)
  {
    col = (_down) ? _button->col_down : _button->col_normal;
    if (_button->round)
      this->buttonDrawRound(_button->left, _button->top, _button->width, _button->height, col);
    else
      this->buttonDrawRect(_button->left, _button->top, _button->width, _button->height, col);
    this->lcd->drawText(_button->left + _button->width / 2 - strlen(_button->caption) * FONT_WIDTH * _button->size / 2,
                        _button->top + _button->height / 2 - FONT_HEIGHT * _button->size / 2,
                        _button->caption, _button->size, _button->col_text, col);

/*
    // buttons with shadow
    if (_down)
    {
      if (_button->round)
      {
        this->buttonDrawRound(_button->left, _button->top, _button->width, _button->height, COLOR_BLACK);
        this->buttonDrawRound(_button->left + BUTTON_SHADOW, _button->top + BUTTON_SHADOW, _button->width, _button->height, _button->col_down);
      }
      else
      {
        this->buttonDrawRect(_button->left, _button->top, _button->width, _button->height, COLOR_BLACK);
        this->buttonDrawRect(_button->left + BUTTON_SHADOW, _button->top + BUTTON_SHADOW, _button->width, _button->height, _button->col_down);
      }
      this->lcd->drawText(_button->left + _button->width / 2 - strlen(_button->caption) * FONT_WIDTH * _button->size / 2 + BUTTON_SHADOW,
                          _button->top + _button->height / 2 - FONT_HEIGHT * _button->size / 2 + BUTTON_SHADOW,
                          _button->caption, _button->size, _button->col_text, _button->col_down);
    }
    else
    {
      if (_button->round)
      {
        this->buttonDrawRound(_button->left + BUTTON_SHADOW, _button->top + BUTTON_SHADOW, _button->width, _button->height, COLOR_BLACK);
        this->buttonDrawRound(_button->left, _button->top, _button->width, _button->height, _button->col_normal);
      }
      else
      {
        this->buttonDrawRect(_button->left + BUTTON_SHADOW, _button->top + BUTTON_SHADOW, _button->width, _button->height, COLOR_BLACK);
        this->buttonDrawRect(_button->left, _button->top, _button->width, _button->height, _button->col_normal);
      }
      this->lcd->drawText(_button->left + _button->width / 2 - strlen(_button->caption) * FONT_WIDTH * _button->size / 2,
                          _button->top + _button->height / 2 - FONT_HEIGHT * _button->size / 2,
                          _button->caption, _button->size, _button->col_text, _button->col_normal);
    }
*/
  }
}

void Touch::buttonDrawRect(uint16_t _l, uint16_t _t, uint16_t _w, uint16_t _h, uint16_t _c)
{
  this->lcd->fillRect(_l, _t, _l + _w - 1, _t + _h - 1, _c);
}

void Touch::buttonDrawRound(uint16_t _l, uint16_t _t, uint16_t _w, uint16_t _h, uint16_t _c)
{
  int16_t x0, y0, x, y, xw, err;

  x0  = _l + _w / 2;
  y0  = _t + _h / 2;
  x   = _h / 2;
  y   = 0;
  xw  = _w / 2 - x;
  err = -x;

  this->lcd->setArea(0, 0, this->lcd->lcd_width - 1, this->lcd->lcd_height - 1);

  while(x >= y)
  {
    this->lcd->drawLine(x0 - x - xw, y0 + y, x0 + x + xw - 1, y0 + y, _c);
    this->lcd->drawLine(x0 - x - xw, y0 - y, x0 + x + xw - 1, y0 - y, _c);
    this->lcd->drawLine(x0 - y - xw, y0 + x, x0 + y + xw - 1, y0 + x, _c);
    this->lcd->drawLine(x0 - y - xw, y0 - x, x0 + y + xw - 1, y0 - x, _c);

    err += y;
    y++;
    err += y;
    if(err >= 0)
    {
      x--;
      err -= x;
      err -= x;
    }
  }
}

TouchButtonPtr Touch::buttonFromPos(uint16_t _x, uint16_t _y)
{
  TouchButtonPtr btn = (TouchButtonPtr)this->buttons;
  while (btn != NULL)
  {
    if ((_x >= btn->left) && (_x < btn->left + btn->width) &&
        (_y >= btn->top)  && (_y < btn->top  + btn->height))
    {
      return btn;
    }
    btn = (TouchButtonPtr)btn->next;
  }
  return NULL;
}

void Touch::buttonState(TouchButtonPtr _current)
{
  if (_current != NULL)
  {
    if (_current->toggle)
    {
      if (_current->group == 0)
      {
        _current->down = !_current->down;
        this->buttonDraw(_current, _current->down);
      }
      else
      {
        if (!_current->down)
          this->buttonDraw(_current, true);
        _current->down = true;
      }
    }
  }
  TouchButtonPtr btn = (TouchButtonPtr)this->buttons;
  while (btn != NULL)
  {
    if ((btn != _current) && btn->toggle && (btn->group > 0) && (btn->group == _current->group))
    {
      if (btn->down)
        this->buttonDraw(btn, false);
      btn->down = false;
    }
    btn = (TouchButtonPtr)btn->next;
  }
}

bool Touch::checkPressure(bool _pressed)
{
  if (this->tp->getPressure() >= this->pressure)
  {
    if (!_pressed)
    {
      for (uint8_t i = 0; i < 10; i++)
      {
        delay(10);
        if (this->tp->getPressure() < this->pressure)
          return false;
      }
      return true;
    }
    else
      return true;
  }
  else
  {
    if (_pressed)
    {
      for (uint8_t i = 0; i < 10; i++)
      {
        delay(10);
        if (this->tp->getPressure() >= this->pressure)
          return true;
      }
      return false;
    }
    else
      return false;
  }
}


// *** public functions ******************************************************

Touch::Touch(lcdPtr _lcd, tpPtr _tp, uint8_t _pressure)
{
  this->lcd      = _lcd;
  this->tp       = _tp;
  this->pressure = _pressure;
  this->buttons  = NULL;
  this->startBtn = NULL;
  this->lastBtn  = NULL;
  this->pressed  = false;
  this->startX   = 0;
  this->startY   = 0;
  this->posX     = 0;
  this->posY     = 0;
}

TouchButtonPtr Touch::buttonAdd(uint16_t _left, uint16_t _top, uint16_t _width, uint16_t _height, uint16_t _col_normal, uint16_t _col_down, uint16_t _col_text, bool _round, bool _toggle, uint8_t _group, uint8_t _size, char *_caption, uint16_t _tag)
{
  TouchButtonPtr btn = (TouchButtonPtr)malloc(sizeof(TouchButton));
  btn->next          = NULL;
  btn->left          = _left;
  btn->top           = _top;
  btn->width         = _width;
  btn->height        = _height;
  btn->col_normal    = _col_normal;
  btn->col_down      = _col_down;
  btn->col_text      = _col_text;
  btn->size          = _size;
  btn->group         = _group;
  btn->round         = _round;
  btn->toggle        = _toggle;
  btn->down          = false;
  strcpy(btn->caption, _caption);
  btn->tag           = _tag;
  if (this->buttons == NULL)
    this->buttons = btn;
  else
  {
    TouchButtonPtr nxt = (TouchButtonPtr)this->buttons;
    while (nxt->next != NULL)
      nxt = (TouchButtonPtr)nxt->next;
    nxt->next = btn;
  }
  return btn;
}

bool Touch::buttonDown(TouchButtonPtr _button)
{
  if (_button != NULL)
    return _button->down;
  else
    return false;
}

TouchButtonPtr Touch::buttonGetFromTag(uint16_t _tag)
{
  TouchButtonPtr btn = (TouchButtonPtr)this->buttons;
  while (btn != NULL)
  {
    if (btn->tag == _tag)
        return btn;
    btn = (TouchButtonPtr)btn->next;
  }
  return NULL;
}

void Touch::buttonSetDown(TouchButtonPtr _button, bool _down)
{
  if ((_button != NULL) && (_button->down != _down))
  {
    if (_down && _button->toggle && (_button->group != 0))
      this->buttonState(_button);
    else
    {
      _button->down = _down;
      this->buttonDraw(_button, _down);
    }
  }
}

void Touch::buttonsClear()
{
  TouchButtonPtr btn;
  while (this->buttons != NULL)
  {
    btn           = (TouchButtonPtr)this->buttons;
    this->buttons = (TouchButtonPtr)this->buttons->next;
    free(btn);
  }
}

void Touch::buttonsDraw()
{
  TouchButtonPtr btn = (TouchButtonPtr)this->buttons;
  while (btn != NULL)
  {
    this->buttonDraw(btn, btn->down);
    btn = (TouchButtonPtr)btn->next;
  }
}

TouchButtonPtr Touch::check()
{
  TouchButtonPtr selBtn = NULL;
  if (checkPressure(this->pressed))
  {
    this->posX = this->tp->getX();
    this->posY = this->tp->getY();
    if (!this->pressed)
    {
      this->pressed  = true;
      this->startX   = this->posX;
      this->startY   = this->posY;
      this->startBtn = this->buttonFromPos(this->startX, this->startY);
      this->lastBtn  = this->startBtn;
      this->buttonDraw(this->startBtn, true);
    }
    else
    {
      if ((this->posY != this->startX) || (this->posY != this->startY))
      {
        TouchButtonPtr currBtn = this->buttonFromPos(this->posX, this->posY);
        if (currBtn != this->startBtn)
          currBtn = NULL;
        if (currBtn != this->lastBtn)
        {
          this->buttonDraw(this->lastBtn, false);
          this->buttonDraw(currBtn, true);
          this->lastBtn = currBtn;
        }
      }
    }
  }
  else
  {
    if (this->pressed)
    {
      if (this->buttonFromPos(this->posX, this->posY) == this->startBtn)
      {
        selBtn = this->startBtn;
        this->buttonState(selBtn);
        if (!selBtn->down)
          this->buttonDraw(selBtn, false);
      }
      else
        this->buttonState(NULL);
      this->pressed = false;
    }
  }
  return selBtn;
}
